home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / SCSI Samples 1.0 / SCSI Inquiry Samples ƒ / SCSI Inquiry (Simple) 06⁄07 ƒ / Src / DoSCSICommand.c next >
Encoding:
C/C++ Source or Header  |  1994-06-16  |  7.4 KB  |  217 lines  |  [TEXT/KAHL]

  1. /*                                DoSCSICommand.c                                    */
  2. /*
  3.  * OriginalSCSI.c
  4.  * Copyright © 1992-93 Apple Computer Inc. All Rights Reserved.
  5.  * This is a simple sample for the original SCSI manager that shows how to
  6.  * arbitrate for the SCSI bus and send a command using the original SCSI manager.
  7.  * This function is incomplete in that it does not support virtual memory and
  8.  * presumes a six-byte SCSI command block.
  9.  *
  10.  * This sample is further limited in that it always does polled reads.
  11.  *
  12.  * Calling Sequence:
  13.  *        OSErr                DoSCSICommand(
  14.  *                unsigned short            targetID,
  15.  *                const Ptr                scsiCommand,
  16.  *                SCSIInstr                requestTIB
  17.  *            );
  18.  * The parameters have the following meaning:
  19.  *
  20.  *    targetID            The SCSI Bus ID of the target (0 .. 6). Note that this
  21.  *                        function can only access LUN zero.
  22.  *    scsiCommand            The SCSI Command Block (6 bytes).
  23.  *    requestTIB            The transfer request block.
  24.  *
  25.  * Return codes:
  26.  *    noErr            normal
  27.  *    scCommErr        no such device (selection error)
  28.  *    scPhaseErr        user data buffer was the wrong size for the transfer.
  29.  *                    You may merely have given a large buffer size to
  30.  *                    a variable-length request, such as Device Inquiry.
  31.  *    sc...            other SCSI error.
  32.  *    statusErr        Device returned "Check condition." The caller should
  33.  *                    issue a Request Sense SCSI Command to this device.
  34.  *    controlErr        Device returned "Busy"
  35.  *    ioErr            Other (serious) device status.
  36.  */
  37. #include <scsi.h>
  38. #include <Errors.h>
  39. #include <OSUtils.h>
  40. #include <Events.h>
  41.  
  42. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  43.  * SCSI command status (from status phase)
  44.  */
  45. #define     kScsiStatusGood            0x00            /* Normal completion        */
  46. #define     kScsiStatusCheckCondition    0x02            /* Need GetExtendedStatus    */
  47. #define     kScsiStatusConditionMet    0x04            /* For Compare Command?        */
  48. #define     kScsiStatusBusy            0x08            /* Device busy (self-test?)    */
  49. #define     kScsiStatusIntermediate    0x10            /* Intermediate status        */
  50. #define     kScsiStatusResConflict        0x18            /* Reservation conflict        */
  51. #define     kScsiStatusQueueFull        0x28            /* Target can't do command    */
  52. #define     kScsiStatusReservedMask    0x3e            /* Vendor specific?            */
  53.  
  54. /*
  55.  * This is the maximum number of times we try to grab the SCSI Bus
  56.  */
  57. #define kMaxSCSIRetries                40                /* 10 seconds, 4 times/sec    */
  58. /*
  59.  * This test is TRUE if the SCSI bus status indicates "busy" (which is the case
  60.  * if either the BSY or SEL bit is set).
  61.  */
  62. #ifndef kScsiStatBSY
  63. #define kScsiStatBSY                (1 << 6)
  64. #endif
  65. #ifndef kScsiStatSEL
  66. #define kScsiStatSEL                (1 << 1)
  67. #endif
  68. #define ScsiBusBusy()        ((SCSIStat() & (kScsiStatBSY | kScsiStatSEL)) != 0)
  69.  
  70.  
  71. OSErr                        DoSCSICommand(
  72.         unsigned short            targetID,
  73.         const Ptr                scsiCommand,
  74.         Ptr                        requestTIB
  75.     );
  76.  
  77. OSErr                        DoSCSICommand(
  78.         unsigned short            targetID,
  79.         const Ptr                scsiCommand,
  80.         Ptr                        requestTIB
  81.     )
  82. {
  83.  
  84.         OSErr                    status;                /* Final status                */
  85.         OSErr                    completionStatus;    /* Status from ScsiComplete    */
  86.         short                    totalTries;            /* Get/Select retries        */
  87.         short                    getTries;            /* Get retries                */
  88.         short                    iCount;                /* Bus free counter            */
  89.         unsigned long            watchdog;            /* Timeout after this        */
  90.         short                    stsByte;            /* Status byte from device    */
  91.         short                    msgByte;            /* Message byte from device    */
  92.         
  93.         status = noErr;
  94.         stsByte = msgByte = (-1);                    /* Preset "bad" values        */
  95.         /*
  96.          * Arbitrate for the scsi bus.  This will fail if some other device is
  97.          * accessing the bus at this time (which is unlikely).
  98.          *
  99.          *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
  100.          *** Do not set breakpoints or call any functions that may require   ***
  101.          *** I/O (such as display code that accesses font resources between  ***
  102.          *** SCSIGet and SCSIComplete,                                         ***
  103.          *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
  104.          */
  105.         for (totalTries = 0; totalTries < kMaxSCSIRetries; totalTries++) {
  106.             for (getTries = 0; getTries < 4; getTries++) {
  107.                 /*
  108.                  * Wait for the bus to go free.
  109.                  */
  110.                 watchdog = TickCount() + 300;        /* 5 second timeout            */
  111.                 while (ScsiBusBusy()) {
  112.                     if (TickCount() > watchdog) {
  113.                         status = scArbNBErr;        /* The bus is busy            */
  114.                         goto exit;                    /* Fail                        */
  115.                     }
  116.                 }
  117.                 /*
  118.                  * The bus is free, try to grab it
  119.                  */
  120.                 for (iCount = 0; iCount < 4; iCount++) {
  121.                     if ((status = SCSIGet()) == noErr)
  122.                         break;
  123.                 }
  124.                 if (status == noErr)                /* If we get the bus, leave    */
  125.                     break;                            /* the "getTries" loop         */
  126.                 /*
  127.                  * The bus became busy again. Try to wait for it to go free.
  128.                  */
  129.                 for (iCount = 0; iCount < 100 && ScsiBusBusy(); iCount++)
  130.                     ;
  131.             } /* The getTries loop */
  132.             if (status != noErr) {
  133.                 /*
  134.                  * If the above loop fails, it means that the SCSI Manager
  135.                  * thinks the bus is not busy and not selected, but "someone" has
  136.                  * set the internal semaphore that signals that the SCSI Manager
  137.                  * itself is busy. The application will have to handle this
  138.                  * problem. (We tried getTries * 4 times).
  139.                  */
  140.                 goto exit;
  141.             }
  142.             /*
  143.              * We now own the SCSI bus. Try to select the device.
  144.              */
  145.             if ((status = SCSISelect(targetID)) != noErr)
  146.                 goto exit;
  147.             /*
  148.              * From this point on, we must exit through SCSIComplete() even if an
  149.              * error is detected. Send a command to the selected device. There are
  150.              * several failure modes, including an illegal command (such as a
  151.              * write to a read-only device). If the command failed because of
  152.              * "device busy", we will try it again.
  153.              */
  154.             status = SCSICmd((Ptr) scsiCommand, 6);
  155.             if (status == noErr)
  156.                 status = SCSIRead((Ptr) requestTIB);
  157. finish:
  158.             /*
  159.              * SCSIComplete "runs" the bus-phase algorithm until the bitter end,
  160.              * returning the status and command-completion message bytes..
  161.              */
  162.             completionStatus = SCSIComplete(
  163.                         &stsByte,
  164.                         &msgByte,
  165.                         (5 * 60)                    /* Five second timeout        */
  166.                     );
  167.             /*
  168.              * If we have an error here, return as the "final" status.
  169.              * 
  170.              */
  171.             if (completionStatus != noErr)
  172.                 status = completionStatus;
  173.             else {
  174.                 /*
  175.                  * ScsiComplete is happy. If the device is busy, Pause for 1/4
  176.                  * second and try again.
  177.                  */
  178.                 if (stsByte == kScsiStatusBusy) {
  179.                     Delay(15, (long *) &watchdog);
  180.                     continue;                /* Do next totalTries attempt        */
  181.                 }
  182.             }
  183.             /*
  184.              * This is the normal exit (success) or final failure exit.
  185.              */
  186.             break;
  187.         } /* totalTries loop */
  188. exit:
  189.         /*
  190.          * Return an artificial error if the device returns a non-zero status:
  191.          *    statusErr        Caller should issue RequestSense.
  192.          *    controlErr        Device is busy (self-test?) try again later.
  193.          *    ioErr            Something is dreadfully wrong.
  194.          *    scPhaseErr        If the device returned good status, the user buffer
  195.          *                    was larger than was needed. (This will be the case
  196.          *                    for an inquiry or request sense command.) Since the
  197.          *                    caller can determine the actual length by examining
  198.          *                    the TIB or the buffer contents, we can return noErr.
  199.          * Also, there is a bug in the combination of System 7.0.1 and the 53C96
  200.          * that may cause the real SCSI Status Byte to be in the Message byte.
  201.          */
  202.         if (stsByte == kScsiStatusGood
  203.          && msgByte == kScsiStatusCheckCondition)
  204.             stsByte = kScsiStatusCheckCondition;
  205.         if (status == scPhaseErr && stsByte == kScsiStatusGood)
  206.             status = noErr;
  207.         if (status == noErr) {
  208.             switch (stsByte) {
  209.             case kScsiStatusGood:                                    break;
  210.             case kScsiStatusCheckCondition:    status = statusErr;        break;
  211.             case kScsiStatusBusy:            status = controlErr;    break;
  212.             default:                        status = ioErr;            break;
  213.             }
  214.         }
  215.         return (status);
  216. }
  217.